Resynchronizing Jittery AES Power Traces

What happens if things aren't as clean as we made them out to be? While we can use preprocessing modules!

Capturing Jittery Traces

Rebuilding New Firmware

In file chipwhisperer/hardware/victims/firmware/simpleserial-aes/simpleserial-aes.c find this:

uint8_t get_pt(uint8_t* pt)
{
    trigger_high();
    aes_indep_enc(pt); /* encrypting the data block */
    trigger_low();
    simpleserial_put('r', 16, pt);
    return 0x00;
}

and add some random delay:

uint8_t get_pt(uint8_t* pt)
{
    trigger_high();
       for(volatile uint8_t k = 0; k < (*pt & 0x0F); k++);
    aes_indep_enc(pt); /* encrypting the data block */
    trigger_low();
    simpleserial_put('r', 16, pt);
    return 0x00;
}

This deterministic delay is NOT a good countermeasure, but is much easier to write in a single line since we don’t have a CSPRNG linked in. We’ll break the jitter without relying on the deterministic aspect though, so our attack would work against a better jitter source.

Be sure to remove this function afterwards so you don't break your code!

We can build the code (change the platform as needed), and confirm the output of the following works as you expect:

In [1]:
%%bash
cd ../../hardware/victims/firmware/
mkdir -p simpleserial-aes-lab1 && cp -r simpleserial-aes/* $_
In [2]:
%%bash
cd ../../hardware/victims/firmware/simpleserial-aes-lab1
awk '{gsub(/trigger_high\(\);/, "trigger_high\(\);\n\tfor\(volatile uint8_t k = 0; k < \(*pt \\& 0x0F\); k++\);\n")}1' simpleserial-aes.c > tmp.c 2>/dev/null
rm simpleserial-aes.c
mv tmp.c simpleserial-aes.c
In [3]:
%%bash
cd ../../hardware/victims/firmware/simpleserial-aes-lab1
make PLATFORM=CWLITEARM CRYPTO_TARGET=TINYAES128C
rm -f -- simpleserial-aes-CWLITEARM.hex

rm -f -- simpleserial-aes-CWLITEARM.eep

rm -f -- simpleserial-aes-CWLITEARM.cof

rm -f -- simpleserial-aes-CWLITEARM.elf

rm -f -- simpleserial-aes-CWLITEARM.map

rm -f -- simpleserial-aes-CWLITEARM.sym

rm -f -- simpleserial-aes-CWLITEARM.lss

rm -f -- objdir/*.o

rm -f -- objdir/*.lst

rm -f -- simpleserial-aes.s simpleserial.s stm32f3_hal.s stm32f3_hal_lowlevel.s stm32f3_sysmem.s aes.s aes-independant.s

rm -f -- simpleserial-aes.d simpleserial.d stm32f3_hal.d stm32f3_hal_lowlevel.d stm32f3_sysmem.d aes.d aes-independant.d

rm -f -- simpleserial-aes.i simpleserial.i stm32f3_hal.i stm32f3_hal_lowlevel.i stm32f3_sysmem.i aes.i aes-independant.i

mkdir .dep

.

-------- begin --------

arm-none-eabi-gcc (GNU Tools for Arm Embedded Processors 7-2018-q2-update) 7.3.1 20180622 (release) [ARM/embedded-7-branch revision 261907]

Copyright (C) 2017 Free Software Foundation, Inc.

This is free software; see the source for copying conditions.  There is NO

warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.



.

Compiling C: simpleserial-aes.c

arm-none-eabi-gcc -c -mcpu=cortex-m4 -I. -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 -fmessage-length=0 -ffunction-sections -gdwarf-2 -DSS_VER=SS_VER_1_1 -DSTM32F303xC -DSTM32F3 -DSTM32 -DDEBUG -DHAL_TYPE=HAL_stm32f3 -DPLATFORM=CWLITEARM -DTINYAES128C -DF_CPU=7372800UL -Os -funsigned-char -funsigned-bitfields -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=objdir/simpleserial-aes.lst -I.././simpleserial/ -I.././hal -I.././hal/stm32f3 -I.././hal/stm32f3/CMSIS -I.././hal/stm32f3/CMSIS/core -I.././hal/stm32f3/CMSIS/device -I.././hal/stm32f4/Legacy -I.././crypto/ -I.././crypto/tiny-AES128-C -std=gnu99 -MMD -MP -MF .dep/simpleserial-aes.o.d simpleserial-aes.c -o objdir/simpleserial-aes.o 

.

Compiling C: .././simpleserial/simpleserial.c

arm-none-eabi-gcc -c -mcpu=cortex-m4 -I. -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 -fmessage-length=0 -ffunction-sections -gdwarf-2 -DSS_VER=SS_VER_1_1 -DSTM32F303xC -DSTM32F3 -DSTM32 -DDEBUG -DHAL_TYPE=HAL_stm32f3 -DPLATFORM=CWLITEARM -DTINYAES128C -DF_CPU=7372800UL -Os -funsigned-char -funsigned-bitfields -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=objdir/simpleserial.lst -I.././simpleserial/ -I.././hal -I.././hal/stm32f3 -I.././hal/stm32f3/CMSIS -I.././hal/stm32f3/CMSIS/core -I.././hal/stm32f3/CMSIS/device -I.././hal/stm32f4/Legacy -I.././crypto/ -I.././crypto/tiny-AES128-C -std=gnu99 -MMD -MP -MF .dep/simpleserial.o.d .././simpleserial/simpleserial.c -o objdir/simpleserial.o 

.

Compiling C: .././hal/stm32f3/stm32f3_hal.c

arm-none-eabi-gcc -c -mcpu=cortex-m4 -I. -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 -fmessage-length=0 -ffunction-sections -gdwarf-2 -DSS_VER=SS_VER_1_1 -DSTM32F303xC -DSTM32F3 -DSTM32 -DDEBUG -DHAL_TYPE=HAL_stm32f3 -DPLATFORM=CWLITEARM -DTINYAES128C -DF_CPU=7372800UL -Os -funsigned-char -funsigned-bitfields -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=objdir/stm32f3_hal.lst -I.././simpleserial/ -I.././hal -I.././hal/stm32f3 -I.././hal/stm32f3/CMSIS -I.././hal/stm32f3/CMSIS/core -I.././hal/stm32f3/CMSIS/device -I.././hal/stm32f4/Legacy -I.././crypto/ -I.././crypto/tiny-AES128-C -std=gnu99 -MMD -MP -MF .dep/stm32f3_hal.o.d .././hal/stm32f3/stm32f3_hal.c -o objdir/stm32f3_hal.o 

.

Compiling C: .././hal/stm32f3/stm32f3_hal_lowlevel.c

arm-none-eabi-gcc -c -mcpu=cortex-m4 -I. -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 -fmessage-length=0 -ffunction-sections -gdwarf-2 -DSS_VER=SS_VER_1_1 -DSTM32F303xC -DSTM32F3 -DSTM32 -DDEBUG -DHAL_TYPE=HAL_stm32f3 -DPLATFORM=CWLITEARM -DTINYAES128C -DF_CPU=7372800UL -Os -funsigned-char -funsigned-bitfields -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=objdir/stm32f3_hal_lowlevel.lst -I.././simpleserial/ -I.././hal -I.././hal/stm32f3 -I.././hal/stm32f3/CMSIS -I.././hal/stm32f3/CMSIS/core -I.././hal/stm32f3/CMSIS/device -I.././hal/stm32f4/Legacy -I.././crypto/ -I.././crypto/tiny-AES128-C -std=gnu99 -MMD -MP -MF .dep/stm32f3_hal_lowlevel.o.d .././hal/stm32f3/stm32f3_hal_lowlevel.c -o objdir/stm32f3_hal_lowlevel.o 

.

Compiling C: .././hal/stm32f3/stm32f3_sysmem.c

arm-none-eabi-gcc -c -mcpu=cortex-m4 -I. -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 -fmessage-length=0 -ffunction-sections -gdwarf-2 -DSS_VER=SS_VER_1_1 -DSTM32F303xC -DSTM32F3 -DSTM32 -DDEBUG -DHAL_TYPE=HAL_stm32f3 -DPLATFORM=CWLITEARM -DTINYAES128C -DF_CPU=7372800UL -Os -funsigned-char -funsigned-bitfields -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=objdir/stm32f3_sysmem.lst -I.././simpleserial/ -I.././hal -I.././hal/stm32f3 -I.././hal/stm32f3/CMSIS -I.././hal/stm32f3/CMSIS/core -I.././hal/stm32f3/CMSIS/device -I.././hal/stm32f4/Legacy -I.././crypto/ -I.././crypto/tiny-AES128-C -std=gnu99 -MMD -MP -MF .dep/stm32f3_sysmem.o.d .././hal/stm32f3/stm32f3_sysmem.c -o objdir/stm32f3_sysmem.o 

.

Compiling C: .././crypto/tiny-AES128-C/aes.c

arm-none-eabi-gcc -c -mcpu=cortex-m4 -I. -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 -fmessage-length=0 -ffunction-sections -gdwarf-2 -DSS_VER=SS_VER_1_1 -DSTM32F303xC -DSTM32F3 -DSTM32 -DDEBUG -DHAL_TYPE=HAL_stm32f3 -DPLATFORM=CWLITEARM -DTINYAES128C -DF_CPU=7372800UL -Os -funsigned-char -funsigned-bitfields -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=objdir/aes.lst -I.././simpleserial/ -I.././hal -I.././hal/stm32f3 -I.././hal/stm32f3/CMSIS -I.././hal/stm32f3/CMSIS/core -I.././hal/stm32f3/CMSIS/device -I.././hal/stm32f4/Legacy -I.././crypto/ -I.././crypto/tiny-AES128-C -std=gnu99 -MMD -MP -MF .dep/aes.o.d .././crypto/tiny-AES128-C/aes.c -o objdir/aes.o 

.

Compiling C: .././crypto/aes-independant.c

arm-none-eabi-gcc -c -mcpu=cortex-m4 -I. -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 -fmessage-length=0 -ffunction-sections -gdwarf-2 -DSS_VER=SS_VER_1_1 -DSTM32F303xC -DSTM32F3 -DSTM32 -DDEBUG -DHAL_TYPE=HAL_stm32f3 -DPLATFORM=CWLITEARM -DTINYAES128C -DF_CPU=7372800UL -Os -funsigned-char -funsigned-bitfields -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=objdir/aes-independant.lst -I.././simpleserial/ -I.././hal -I.././hal/stm32f3 -I.././hal/stm32f3/CMSIS -I.././hal/stm32f3/CMSIS/core -I.././hal/stm32f3/CMSIS/device -I.././hal/stm32f4/Legacy -I.././crypto/ -I.././crypto/tiny-AES128-C -std=gnu99 -MMD -MP -MF .dep/aes-independant.o.d .././crypto/aes-independant.c -o objdir/aes-independant.o 

.

Assembling: .././hal/stm32f3/stm32f3_startup.S

arm-none-eabi-gcc -c -mcpu=cortex-m4 -I. -x assembler-with-cpp -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 -fmessage-length=0 -ffunction-sections -DF_CPU=7372800 -Wa,-gstabs,-adhlns=objdir/stm32f3_startup.lst -I.././simpleserial/ -I.././hal -I.././hal/stm32f3 -I.././hal/stm32f3/CMSIS -I.././hal/stm32f3/CMSIS/core -I.././hal/stm32f3/CMSIS/device -I.././hal/stm32f4/Legacy -I.././crypto/ -I.././crypto/tiny-AES128-C .././hal/stm32f3/stm32f3_startup.S -o objdir/stm32f3_startup.o

.

Linking: simpleserial-aes-CWLITEARM.elf

arm-none-eabi-gcc -mcpu=cortex-m4 -I. -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 -fmessage-length=0 -ffunction-sections -gdwarf-2 -DSS_VER=SS_VER_1_1 -DSTM32F303xC -DSTM32F3 -DSTM32 -DDEBUG -DHAL_TYPE=HAL_stm32f3 -DPLATFORM=CWLITEARM -DTINYAES128C -DF_CPU=7372800UL -Os -funsigned-char -funsigned-bitfields -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=objdir/simpleserial-aes.o -I.././simpleserial/ -I.././hal -I.././hal/stm32f3 -I.././hal/stm32f3/CMSIS -I.././hal/stm32f3/CMSIS/core -I.././hal/stm32f3/CMSIS/device -I.././hal/stm32f4/Legacy -I.././crypto/ -I.././crypto/tiny-AES128-C -std=gnu99 -MMD -MP -MF .dep/simpleserial-aes-CWLITEARM.elf.d objdir/simpleserial-aes.o objdir/simpleserial.o objdir/stm32f3_hal.o objdir/stm32f3_hal_lowlevel.o objdir/stm32f3_sysmem.o objdir/aes.o objdir/aes-independant.o objdir/stm32f3_startup.o --output simpleserial-aes-CWLITEARM.elf --specs=nano.specs -T .././hal/stm32f3/LinkerScript.ld -Wl,--gc-sections -lm -Wl,-Map=simpleserial-aes-CWLITEARM.map,--cref   -lm  

.

Creating load file for Flash: simpleserial-aes-CWLITEARM.hex

arm-none-eabi-objcopy -O ihex -R .eeprom -R .fuse -R .lock -R .signature simpleserial-aes-CWLITEARM.elf simpleserial-aes-CWLITEARM.hex

.

Creating load file for EEPROM: simpleserial-aes-CWLITEARM.eep

arm-none-eabi-objcopy -j .eeprom --set-section-flags=.eeprom="alloc,load" \

	--change-section-lma .eeprom=0 --no-change-warnings -O ihex simpleserial-aes-CWLITEARM.elf simpleserial-aes-CWLITEARM.eep || exit 0

.

Creating Extended Listing: simpleserial-aes-CWLITEARM.lss

arm-none-eabi-objdump -h -S -z simpleserial-aes-CWLITEARM.elf > simpleserial-aes-CWLITEARM.lss

.

Creating Symbol Table: simpleserial-aes-CWLITEARM.sym

arm-none-eabi-nm -n simpleserial-aes-CWLITEARM.elf > simpleserial-aes-CWLITEARM.sym

Size after:

   text	   data	    bss	    dec	    hex	filename

   5384	    532	   1484	   7400	   1ce8	simpleserial-aes-CWLITEARM.elf

+--------------------------------------------------------

+ Built for platform CW-Lite Arm (STM32F3)

+--------------------------------------------------------

Setup

Now let's go ahead. We'll have to program the file we built, so be sure to confirm we are using the right file!

In [4]:
import chipwhisperer as cw
scope = cw.scope()
target = cw.target(scope)
In [5]:
%run "Helper_Scripts/Setup_Target_Generic.ipynb"
In [6]:
import os, time

# uncomment based on your target
#%run "Helper_Scripts/Program_XMEGA.ipynb"
%run "Helper_Scripts/Program_STM.ipynb"
#%run "Helper_Scripts/No_Programmer.ipynb"
fw_path = "../../hardware/victims/firmware/simpleserial-aes-lab1/simpleserial-aes-CWLITEARM.hex"

modtime = os.path.getmtime(fw_path)
print("File build time: {:s} (built {:.2f} mins ago)".format(str(time.ctime(modtime)), (time.time() - modtime)/60.0))
File build time: Wed Dec  5 12:33:13 2018 (built 0.04 mins ago)
In [7]:
# program the target
program_target(scope, fw_path)
Detected known STMF32: STM32F302xB(C)/303xB(C)
Extended erase (0x44), this can take ten seconds or more
Attempting to programming 5915 bytes at 0x8000000
STM32F Programming flash...
STM32F Reading flash...
Verified flash OK, 5915 bytes

In addition, before we capture our traces, we'll need to create a ChipWhipserer project, since that's what Analyzer expects for an input:

In [8]:
project = cw.createProject("projects/jupyter_test_jittertime.cwp", overwrite = True)

And we can get the class used to hold our traces by:

In [9]:
from datetime import datetime
import copy

tc = copy.copy(project.getTraceFormat())
starttime = datetime.now()
prefix = starttime.strftime('%Y.%m.%d-%H.%M.%S') + "_"
tc.config.setConfigFilename(project.datadirectory + "traces/config_" + prefix + ".cfg")
tc.config.setAttr("prefix", prefix)
tc.config.setAttr("date", starttime.strftime('%Y-%m-%d %H:%M:%S'))

Capturing Traces

Below you can see the capture loop. The main body of the loop loads some new plaintext, arms the scope, sends the key and plaintext, then finally records and our new trace into our trace class. We'll also keep track of our keys manually for checking our answer later.

In [10]:
#Capture Traces
from tqdm import tqdm
import numpy as np
import time

ktp = cw.ktp.Basic(target=target)

keys = []
N = 50  # Number of traces
target.init()
for i in tqdm(range(N), desc='Capturing traces'):
    # run aux stuff that should come before trace here

    key, text = ktp.newPair()  # manual creation of a key, text pair can be substituted here
    keys.append(key)

    #target.reinit()

    target.setModeEncrypt()  # only does something for targets that support it
    target.loadEncryptionKey(key)
    target.loadInput(text)

    # run aux stuff that should run before the scope arms here

    scope.arm()

    # run aux stuff that should run after the scope arms here

    target.go()
    timeout = 50
    # wait for target to finish
    while target.isDone() is False and timeout:
        timeout -= 1
        time.sleep(0.01)

    try:
        ret = scope.capture()
        if ret:
            print('Timeout happened during acquisition')
    except IOError as e:
        print('IOError: %s' % str(e))

    # run aux stuff that should happen after trace here
    _ = target.readOutput()  # clears the response from the serial port
    #traces.append(scope.getLastTrace())
    tc.addTrace(scope.getLastTrace(), text, "", key)
Capturing traces: 100%|██████████| 50/50 [00:10<00:00,  4.93it/s]

Now that we have our traces, we need to tell the project that the traces are loaded and add them to the project's trace manager.

In [11]:
#Calling closeAll() to ensure flush to disk happens
tc.closeAll()

project.traceManager().appendSegment(tc)

#Save project file
project.save()

We're now done with the ChipWhisperer hardware, so we should disconnect from the scope and target:

In [12]:
# cleanup the connection to the target and scope
scope.dis()
target.dis()

Analysis

To fix the jitter, we'll need to add our traces to a preprocessing module. We can feed project.traceManager() right into attack.setTraceSource(), but we could also add pre-processing inbetween (more about this later). We'll also re-open the traces, in this case it is required since the call to closeAll() would have flushed the buffers.

In [13]:
#Force reload of project data (if you comment out 'closeAll()' this isn't needed)

#We also rebuild the project object in case you only want to run this half
import chipwhisperer as cw
project = cw.openProject("projects/jupyter_test_jittertime.cwp")

This time we're going to do a few things. First we will get the traces, and plot a few of them as-is. You can adjust the traces plotted by adjusting the range(10). For example range(1) plots the first trace.

In [14]:
tm = project.traceManager()

from bokeh.plotting import figure, show
from bokeh.io import output_notebook
from bokeh.palettes import Dark2_5 as palette
import itertools  

output_notebook()
p = figure(sizing_mode='scale_width', plot_height=300)

# create a color iterator
colors = itertools.cycle(palette)  

x_range = range(0, tm.numPoints())
for i, color in zip(range(10), colors): #Adjust range(n) to plot certain traces
    p.line(x_range, tm.getTrace(i), color=color)
show(p)
Loading BokehJS ...

So how do we fix that? To begin with, you should plot only a single trace to make your life more clear. You'll need to figure out a very unique area. For example see the following figure showing a single plot. In this example the location of A is unique, but B would have many matches within that same trace, even nearby: Resync example trace

We will specify two items:

  • A window with the "unique" area defined.
  • How far we will shift the window (+/- points) to search for the best match.

You can use the following code to define the target_window and max_shift. Try a few values until you find something that works. Also try some poor example, and also try plotting more traces to confirm your match is working in real life.

In [15]:
from chipwhisperer.analyzer.preprocessing.resync_sad import ResyncSAD #update with new interface

resync_traces = ResyncSAD(tm, connectTracePlot=False)
resync_traces.enabled = True
resync_traces.ref_trace = 0

#Define a target window here. 500,900 for example is good based on above. But try some different values.
resync_traces.target_window = (700, 1500)

# Define max_shift. Must not cause target_window to go outside of valid data. Try 16-600 range. Ideal value varies with how
# much jitter is in original data. 
resync_traces.max_shift = 700

#Uses objects from previous cells (plotting etc), so 
output_notebook()
p = figure()

for i, color in zip(range(10), colors):
    p.line(x_range, resync_traces.getTrace(i), color=color)
show(p)

preprocessed_traces = resync_traces
Loading BokehJS ...

If this all works - let's just continue the attack! Do so as below:

In [16]:
attack = cw.CPA()
N = 50 #number of traces

leak_model = cw.AES128(cw.AES128Leakage.SBox_output)
attack.setAnalysisAlgorithm(cw.CPAProgressive, leak_model)
attack.setTraceSource(preprocessed_traces, addToList=True) ##THIS LINE CHANGED TO INPUT TRACES
attack.setTraceStart(0)
attack.setTracesPerAttack(project.traceManager().numTraces())
attack.setIterations(1)
attack.setReportingInterval(10)
attack.setTargetSubkeys([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15])
attack.setPointRange((0, -1))

And then actually run it:

In [17]:
cb = cw.getJupyterCallback(attack)
attack_results = attack.processTracesNoGUI(cb)
Finished traces 40 to 50
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
PGE= 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 2B
0.867
7E
0.895
15
0.909
16
0.887
28
0.809
AE
0.881
D2
0.929
A6
0.913
AB
0.937
F7
0.878
15
0.841
88
0.864
09
0.866
CF
0.864
4F
0.872
3C
0.840
1 62
0.621
9E
0.647
45
0.719
61
0.664
4C
0.652
87
0.666
04
0.621
9A
0.621
8D
0.641
B4
0.650
CC
0.609
7D
0.664
3C
0.607
5A
0.656
96
0.603
84
0.617
2 73
0.615
85
0.626
39
0.647
C6
0.629
A3
0.612
5A
0.650
AD
0.611
27
0.615
35
0.630
EA
0.635
B6
0.607
85
0.630
2D
0.588
61
0.605
72
0.601
EA
0.608
3 4D
0.609
E3
0.601
98
0.639
30
0.626
C5
0.610
F0
0.599
6C
0.611
0D
0.608
53
0.627
DA
0.601
C3
0.607
32
0.630
E8
0.585
85
0.602
F5
0.597
26
0.593
4 16
0.603
E6
0.599
E2
0.599
C1
0.621
CD
0.610
D9
0.598
9A
0.611
72
0.603
47
0.621
18
0.586
2D
0.591
FD
0.609
41
0.580
B7
0.598
1B
0.589
22
0.592

You should see the PGE reach 0 for each byte. If not, you might need to adjust the SAD resync. You could also need to increase the length of the sample capture for example. You may notice that it starts working OK and then fails, due to later traces become unsychronized.

Plotting Correlation Output

In [18]:
from bokeh.plotting import figure, show
from bokeh.io import output_notebook

attack_results = attack.getStatistics()
plot_data = cw.analyzerPlots(attack_results)
bnum = 0

ret = plot_data.outputVsTime(bnum)

output_notebook()
p = figure()
p.line(ret[0], ret[2], line_color='green')
p.line(ret[0], ret[3], line_color='green')

p.line(ret[0], ret[1], line_color='red')
show(p)
Loading BokehJS ...

You should see a graph of red and green in time (samples). In red is the correlation of the correct subkey for the first byte, while the rest are in green.

You should see two or three distinctive red spikes. The first is the spot where the sbox lookup for the subkey we guessed actually happens (the later ones are from later steps in the AES operation).

What about the rest of the bytes in the key? We can get and plot that easily as well:

In [19]:
rets = []
for i in range(0, 16):
    rets.append(plot_data.outputVsTime(i))

p = figure()
for ret in rets:
    p.line(ret[0], ret[2], line_color='green')
    p.line(ret[0], ret[3], line_color='green')
    
for ret in rets:
    p.line(ret[0], ret[1], line_color='red')

show(p)

Conclusion

Awesome! You should have now completed a resynchronization of power traces. This is a very useful tool, and you can see how making a simple class could extend this work.

Tests

In [20]:
key = project.traceManager().getKnownKey(0)
recv_key = [kguess[0][0] for kguess in attack_results.findMaximums()]
assert (key == recv_key).all(), "Failed to recover encryption key\nGot: {}\nExpected: {}".format(recv_key, key)
In [21]:
assert (attack_results.pge == [0]*16), "PGE for some bytes not zero: {}".format(attack_results.pge)
In [22]:
max_corrs = [kguess[0][2] for kguess in attack_results.findMaximums()]
assert (np.all([corr > 0.75 for corr in max_corrs])), "Low correlation in attack (corr <= 0.75): {}".format(max_corrs)